
package com.example.filetransfer.publicsource.connection;


import com.example.filetransfer.publicsource.Log;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;

public class DataSender extends Thread {
    private static final String TAG = "DataSender";
    // Save the task.
    private List<DataSendTask> mDataToSend = new ArrayList<DataSendTask>();
    private OutputStream mOutputStream;
    private volatile boolean mStop = false;
    private static final int MAX_TASK_SZIE = 1000;
    private DataOutputStream dos = null;
    private DataSendTask task = null;
    // The max time to wait for OutputStream 1min
    private OnSocketError mSocketErrorListener = null;
    private String mRootPath = null;

    public enum SenderType {
        TYPE_CLIENT, TYPE_SERVER
    }

    /**
     * Need set the output sream later.
     * @param rootPath
     */
    public DataSender(String rootPath) {
        this(null, rootPath);
    }

    public DataSender(OutputStream outputStream, String rootPath) {
        this.mOutputStream = outputStream;
        this.mRootPath = rootPath;
    }

    public void setOutputStream(OutputStream outputStream) {
        this.mOutputStream = outputStream;
    }

    public void setSocketErrorListener(OnSocketError socketError) {
        this.mSocketErrorListener = socketError;
    }

    private boolean addTask(DataSendTask task) {
        synchronized (mDataToSend) {
            if (mDataToSend.size() <= MAX_TASK_SZIE) {
                mDataToSend.add(task);
                // Notify the waiting, To stop timer , and start work.
                mDataToSend.notify();
                return true;
            }
            return false;
        }
    }

    public void sendFile(String fileUri, FileTransferListener listener)
            throws FileNotFoundException {
        DataSendTask task = DataSendTask.createFileTask(fileUri, listener);
        addTask(task);
    }

    public void sendMessage(String message) {
        DataSendTask task = DataSendTask.createMessageTask(message);
        addTask(task);
    }

    private DataSendTask fetchTask() {
        synchronized (mDataToSend) {
            if (mDataToSend.size() <= 0) {
                return null;
            }
            return mDataToSend.remove(0);
        }
    }

    public void stopThread() {
        synchronized (mDataToSend) {
            mStop = true;
            // If no work to do,thread will be wait, notify the thread.
            mDataToSend.notify();
        }
    }

    private float lastbili = -1f;

    private void sendFile(DataOutputStream dos, String fileUrl) throws IOException {
        DataInputStream dis = null;
        try {
            File file = new File(Protocol.realURL(fileUrl));
            dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
            long size = file.length();
            dos.writeInt(1);
            dos.flush();
            String pathToSend = Protocol.removeFileRoot(mRootPath, fileUrl);
            System.out.println("yadong --- pathToSend = " + pathToSend);
            dos.writeUTF(pathToSend);
            dos.flush();
            dos.writeLong(size);
            dos.flush();
            int buffferSize = 1024;
            byte[] bufArray = new byte[buffferSize];
            long passedSize = 0;
            while (true) {
                int read = 0;
                if (dis != null) {
                    read = dis.read(bufArray);
                }
                if (read == -1) {
                    break;
                }
                dos.write(bufArray, 0, read);
                passedSize += read;
                long bili = passedSize * 100 / size;
                task.progress = (int) bili;
                if (task.fileSendListener != null && lastbili != bili) {
                    task.fileSendListener.onProgressChanged(fileUrl, task.progress);
                    lastbili = bili;
                }
            }
            dos.flush();
        } finally {
            if (dis != null) {
                dis.close();
            }
        }
    }

    private void sendMessage(DataOutputStream dos, String message) throws IOException {
        dos.writeInt(1);
        dos.flush();
        dos.writeUTF(message);
        dos.flush();
    }

    @Override
    public void run() {
        try {
            synchronized (mDataToSend) {
                while ((task = fetchTask()) != null || !mStop) {
                    // If no work need do, wait.
                    if (task == null) {
                        try {
                            mDataToSend.wait();
                            continue;
                        } catch (InterruptedException e) {
                        }
                    }
                    dos = new DataOutputStream(mOutputStream);
                    String data = task.data;
                    Log.d(TAG, "send data:" + data);
                    if (Protocol.sendFile(data)) {
                        sendFile(dos, data);
                    } else if (Protocol.sendMessage(data)) {
                        sendMessage(dos, data);
                    }
                }
            }
        } catch (SocketTimeoutException exception) {
            if (task.tryTimes < DataSendTask.MAX_TRY_TIMES) {
                task.tryTimes++;
                DataSender.this.addTask(task);
            }
        } catch (IOException e) {
            e.printStackTrace();
            if (mSocketErrorListener != null) {
                mSocketErrorListener.onSocketError(e);
            }
        } finally {
            release();
        }
    }

    public void release() {
        Log.d(TAG, "release sender");
        mStop = true;
        if (mOutputStream != null) {
            try {
                mOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static class DataSendTask {
        public static final int MAX_TRY_TIMES = 3;
        private String data;
        private FileTransferListener fileSendListener;
        private int progress = 0;
        private int tryTimes = 0;

        private DataSendTask(String data, FileTransferListener listener) {
            this.data = data;
            this.fileSendListener = listener;
        }

        public static DataSendTask createFileTask(String data, FileTransferListener listener)
                throws FileNotFoundException {
            data = Protocol.buildFileUrl(data);
            File file = new File(Protocol.realURL(data));
            if (!file.exists() || !file.isFile()) {
                throw new FileNotFoundException();
            }
            return new DataSendTask(data, listener);
        }

        public static DataSendTask createMessageTask(String data) {
            data = Protocol.buildMessage(data);
            return new DataSendTask(data, null);
        }
    }

}
